import requests
import json
import time
from typing import Dict
import hmac_encryption
from caoyongpy import publicdef
import traceback
import mssql
import config
from user import certifyToken
from pres import getPresDetail  # 根据pres_id生成处方详细数据
from presToPdf import presToPdfBase64 # 根据presData生成打印pdf文件的base64串
from datetime import datetime

yb_api_prefix = config.c('yb_api_prefix')  # 医保地址

urls = {
    "9001":"fsi/api/signInSignOutService/signIn",
    "9002":"fsi/api/signInSignOutService/signOut",
    "7101":"fsi/api/opsp/7101",
    "7104":"fsi/api/opsp/7104"
}

def get_dict_code(dict_type, key):
    db = mssql.ZgqMssql(config.get('db'))
    data_code = db.getRow("select * from dict_code where dict_type = '{}' and `key` = '{}'".format(dict_type, key))
    db.close()
    return data_code.get('value')
    
def get_yb_info():
    db = mssql.ZgqMssql(config.get('db'))
    sql = "select hospitalCode,hospitalName,hospitalPasswd,prefix + CASE WHEN RIGHT(prefix,1) = '/' THEN '' ELSE '/' END prefix from " + \
        "\n(select convert(nvarchar(50),csz) as hospitalCode from sys_xtsz where csmc = 'GJYB_YYBH') a," + \
        "\n(select convert(nvarchar(50),csz) as hospitalName from sys_xtsz where csmc = 'GJYB_YYMC') b," + \
        "\n(select convert(nvarchar(50),csz) as hospitalPasswd from sys_xtsz where csmc = 'GJYB_SECRET') c," + \
        "\n(select convert(nvarchar(50),csz) as prefix from sys_xtsz where csmc = 'GJYB_URL_WP') d"
    data_code = db.getRow(sql)
    db.close()

    '''
    # 测试代码
    data_code = {
        "prefix": "http://10.10.114.51:245/",
        "hospitalCode":'H33010903574',
        "hospitalName":'杭州梦香健康管理有限公司萧山工人路诊所',
        "hospitalPasswd":'169FiKwM9vnST7J'
    }
    '''
    return data_code

def get_sign_zj_sm3(request_body, secret, unix_timestamp):
    post_data = {
            "component": "ZJ_CardReader",
            "funcLayerObj": {
                "function": "ZJ_Hmac_SM3",
                "paraLayerObj": {
                    "secret": secret,
                    "request_body": request_body,
                    "key": "key",
                    "unix_timestamp": unix_timestamp
                }
            }
    }

    ret_d = None

    try:
        with requests.post(yb_api_prefix, json=post_data) as req:
            req_dict = req.json()
            ret_d = req_dict['message']['info']
    except:
        pass
    return ret_d


def get_sign(request_body, secret, unix_timestamp):
    data = unix_timestamp+'\n'+request_body
    ret = unix_timestamp+':'+hmac_encryption.sm3_hmac(data.encode().hex(), secret.encode().hex())
    return ret.upper()


def get_public(hospital_code, hospital_name, opter, opter_name):
    d = dict()
    # msgid 格式 发送方报文ID	字符型	长度30 	定点医药机构编号(12)+时间(14)+顺序号(4) 时间格式：yyyyMMddHHmmss
    d['msgid'] = hospital_code+publicdef.to_datetime(to_str='%Y%m%d%H%M%S')+str(time.time()).split('.')[-1][:4].zfill(4)
    d['mdtrtarea_admvs'] = '330109'  # 就医地医保区划
    d['recer_sys_code'] = 'MBS_LOCAL'  # 接收方系统代码	字符型	10		Y	用于多套系统接入，区分不同系统使用
    d['signtype'] = 'SM3'  # 签名类型	 建议使用SM2、SM3
    d['infver'] = 'V1.0'  # 接口版本号 例如：“V1.0”，版本号由医保下发通知。
    d['opter_type'] = '1'  # 经办人类别 1-经办人；2-自助终端；3-移动终端
    d['opter'] = opter #'XSSSJ001'  # 经办人	字符型	30		Y	按地方要求传入经办人/终端编号
    d['opter_name'] = opter_name # '沈生江'  # 经办人姓名 按地方要求传入经办人姓名/终端名称
    d['inf_time'] = publicdef.to_datetime()  # 交易时间	日期时间型  例：2020-12-03 10:00:00
    d['fixmedins_code'] = hospital_code  # 定点医药机构编号
    d['fixmedins_name'] = hospital_name  # 定点医药机构名称
    d['sign_no'] = ''
    d['dev_safe_info'] = ''
    d['dev_no'] = ''
    d['cainfo'] = ''
    d['insuplc_admdvs'] = ''
    d['input'] = {}
    return d

def yb_9001(hospital_code, hospital_name, hospital_password, opter, opter_name ):
    # 获取单位信息
    unitInfo = get_yb_info()
    # 医保电子处方上传接口地址
    url = unitInfo['prefix'] + 'fsi/api/signInSignOutService/signIn'
    sign_no = ''  # 签名内容
    for _ in range(4):  # 循环指定次数
        post_data = get_public(hospital_code,hospital_name,opter,opter_name)
        post_data['infno'] = '9001'  # 交易编号 ，交易编号详见接口列表
        post_data['input'] = {'signIn': {}}
        post_data['input']['signIn']['opter_no'] = opter # 'XSSSJ001'  # 操作员编号
        post_data['input']['signIn']['mac'] = '0.0.0.0'  # 操作员编号
        post_data['input']['signIn']['ip'] = '0.0.0.0'  # 操作员编号
        post_str = json.dumps(post_data)
        sign = get_sign(post_str, hospital_password, str(int(time.time())))
        headers = {'x-ca-key': hospital_code, 'x-ca-signature': sign}
        try:
            with requests.post(url, post_str, headers=headers) as req:
                sign_no = req.json()['output']['signinoutb']['sign_no']
                break
        except:
            traceback.print_exc()
            time.sleep(0.2)
    return sign_no
    
def yb_9002(hospital_code,hospital_name,hospital_password,opter,opter_name,sign_no):
    # 本函数有异常，不知为啥
    # 获取单位信息
    unitInfo = get_yb_info()
    # 医保电子处方上传接口地址  
    url = unitInfo['prefix'] + 'fsi/api/signInSignOutService/signOut'
    for _ in range(4):  # 循环指定次数
        post_data = get_public(hospital_code, hospital_name, opter, opter_name)
        post_data['infno'] = '9002'  # 交易编号 ，交易编号详见接口列表
        post_data['input'] = {'signOut': {}}
        post_data['input']['signOut']['opter_no'] = opter # 'XSSSJ001'  # 操作员编号
        post_data['input']['signOut']['sign_no'] = sign_no  # 操作员编号
        post_str = json.dumps(post_data)
        sign = get_sign(post_str, hospital_password, str(int(time.time())))
        headers = {'x-ca-key': hospital_code, 'x-ca-signature': sign}
        try:
            with requests.post(url, post_str, headers=headers) as req:
                ret = req.json()
                print(ret)
                break
        except:
            traceback.print_exc()
            time.sleep(0.2)
    return ret

# 处方上传data部分
def data_by_presid__7101(presId:str):
    # 生成data数据    s_id = gjyb_jsjl.get('JSID')
    # p_no = gjyb_jsjl.get('GRBH')
    # 如果有社保卡号的按社保卡号上传，免了需要读电子医保凭证
    sql = "SELECT case when GJYB_JSJL.grbh > '' then '03' else GJYB_JSJL.pzlx end mdtrt_cert_type," + \
        "\n GJYB_JSJL.JZID mdtrt_cert_no," + \
        "\n GJYB_JSJL.grbh card_sn," + \
        "\n '01' biz_type_code,pres_id hosp_rxno, " + \
        "\n '1' rx_type_code, pres_date prsc_time," + \
        "\n medi_count rx_drug_cnt, valid_days, convert(varchar(20),pres_date + valid_days,120) valid_end_time," + \
        "\n '0' rx_cotn_flag, '' original_rx_file" + \
        "\n FROM pres_out" + \
        "\n INNER JOIN GJYB_JSJL ON pres_out.register_pres = GJYB_JSJL.FPHM" + \
        "\n WHERE pres_id = '{}'".format(presId)
    print(sql)
    db = mssql.ZgqMssql(config.get('db'))
    data = db.getRow(sql)
    db.close()
    # 根据presId生成处方打印pdf文件的BASE64
    presData=getPresDetail(presId)['data']
    original_rx_file=presToPdfBase64(presData)
    data['original_rx_file']=original_rx_file
    return data
    
# 处方上传diseinfo部分
def diseinfo_by_presid__7101(presId:str):
    sql = "SELECT '1' diag_type, icds, convert(nvarchar(500),icdnames) icdnames," + \
        " convert(nvarchar(500),sect.name) diag_dept, " + \
        " staff_member.country_code diag_dr_no,convert(nvarchar(500),staff_member.name) diag_dr_name," + \
        " pres_out.pres_date diag_time" + \
        " FROM pres_out " + \
        " INNER JOIN sect ON sect.sect_id = pres_out.sect_id" + \
        " INNER JOIN staff_member ON staff_member.staff_id = doct_id" + \
        " WHERE pres_id = '{}'".format(presId)
    db = mssql.ZgqMssql(config.get('db'))
    data = db.getRow(sql)

    # 拆分诊断为多条记录
    icds = data['icds'].split(',')
    icdnames = data['icdnames'].split(',')
    diseinfo = []
    for i in range(len(icds)):  # 循环指定次数
        sql = "select code, case when zyzd = 0 then 1 else 2 end diag_type from xsnb_icd10 where code = '{}'".format(icds[i])
        d = db.getRow(sql)
        diseinfo.append({
            "diag_type":d['diag_type'],
            "maindiag_flag":1 if i==0 else 0,
            "diag_srt_no":i + 1,
            "diag_code":icds[i],
            "diag_name":icdnames[i],
            "diag_dept":data['diag_dept'],
            "diag_dr_no":data['diag_dr_no'],
            "diag_dr_name":data['diag_dr_name'],
            "diag_time":data['diag_time']
        })
    db.close()
    return diseinfo

# 处方上传mdtrtinfo部分
def mdtrtinfo_by_presid__7101(presId:str):
    sql = "SELECT JZID mdtrt_id, '11' med_type," + \
        " patient.patient_id ipt_otp_no," + \
        " GJYB_JSJL.GRBH psn_no," + \
        " convert(nvarchar(500),patient.name) patn_name," + \
        " patient.age patn_age,patient.sex gend," + \
        " GJYB_JSJL.ZJLX psn_cert_type, GJYB_JSJL.ZJHM certno," + \
        " GJYB_JSJL.XZLX insutype, GJYB_JSJL.TCQH insuplc_admdvs," + \
        " convert(nvarchar(500),sect.name) prsc_dept_name," + \
        " sect.sect_id prsc_dept_code," + \
        " staff_member.country_code dr_code," + \
        " convert(nvarchar(500),staff_member.name) prsc_dr_name," + \
        " staff_member.techn_cat_id dr_profttl_codg," + \
        " convert(nvarchar(500),techn_cat.name) dr_profttl_name," + \
        " convert(nvarchar(500),chs.name) phar_dept_name, chs.sect_id phar_dept_code," + \
        " sect.sect_id dr_dept_code,convert(nvarchar(500),sect.name) dr_dept_name," + \
        " ch.country_code phar_code," + \
        " convert(nvarchar(500),ch.name) phar_name," + \
        " ch.techn_cat_id phar_profttl_codg," + \
        " convert(nvarchar(500),cht.name) phar_profttl_name," + \
        " convert(varchar(20),pres_out.sts_date,120) phar_chk_time," + \
        " convert(varchar(20),pres_date,120) mdtrt_time," + \
        " 0 sp_dise_flag," + \
        " CASE WHEN CHARINDEX(',',icds) > 0 THEN substring(icds,1, CHARINDEX(',',icds) - 1) ELSE icds end maindiag_code," + \
        " CONVERT(NVARCHAR(500),CASE WHEN CHARINDEX(',',icdnames) > 0 THEN substring(icdnames,1, CHARINDEX(',',icdnames) - 1) ELSE icdnames end) maindiag_name," + \
        " 0 medfee_sumamt" + \
        " FROM pres_out" + \
        " INNER JOIN patient ON pres_out.patient_id = patient.patient_id" + \
        " INNER JOIN staff_member ON staff_member.staff_id = doct_id" + \
        " INNER JOIN sect ON sect.sect_id = pres_out.sect_id" + \
        " INNER JOIN techn_cat ON  techn_cat.techn_cat_id = staff_member.techn_cat_id" + \
        " INNER JOIN staff_member ch ON ch.staff_id = pres_out.check_id" + \
        " INNER JOIN sect chs ON chs.sect_id = ch.sect_id" + \
        " INNER JOIN techn_cat cht ON cht.techn_cat_id = ch.techn_cat_id" + \
        " INNER JOIN GJYB_JSJL ON pres_out.register_pres = GJYB_JSJL.FPHM" + \
        " WHERE pres_id = '{}'".format(presId)
    db = mssql.ZgqMssql(config.get('db'))
    mdtrtinfo = db.getRow(sql)
    db.close()
    return mdtrtinfo
    
# 处方上传rxdrugdetail部分
def rxdrugdetail_by_presid__7101(presId:str):
    sql = "SELECT " + \
        " medi_mark.country_code med_list_codg, charge_type.ybxmgb rx_item_type_code," + \
        " convert(nvarchar,medi_dict.name) drug_genname," + \
        " CONVERT(NVARCHAR,dose_type.name) drug_dosform," + \
        " CONVERT(NVARCHAR,medi_mark.spec) drug_spec," + \
        " medi_pass.ybdm medc_way_codg," + \
        " CONVERT(NVARCHAR,medi_pass.name) medc_way_dscr," + \
        " pres_out.pres_date medc_starttime," + \
        " pres_out.pres_date + pres_medi_out.zxts medc_endtime," + \
        " pres_medi_out.zxts medc_days," + \
        " quantity drug_totlcnt," + \
        " CONVERT(NVARCHAR,rtrim(medi_unit.name)) drug_dosunt," + \
        " quantity drug_cnt," + \
        " CONVERT(NVARCHAR,rtrim(medi_unit.name)) drug_totlcnt_emp," + \
        " 0 own_expense_flag," + \
        " 1 hosp_appr_flag," + \
        " use_type.use_type_id used_frqu_codg," + \
        " convert(NVARCHAR(500),use_type.name) used_frqu_name," + \
        " pres_medi_out.medi_doage sin_doscnt," + \
        " convert(NVARCHAR(500),medi_unit.name) sin_dosunt" + \
        " FROM pres_out" + \
        " inner join pres_medi_out ON pres_out.pres_id = pres_medi_out.pres_id" + \
        " INNER JOIN medi_mark ON medi_mark.medi_mark_id = pres_medi_out.medi_mark_id" + \
        " INNER JOIN medi_dict ON medi_dict.medi_id = medi_mark.medi_id" + \
        " INNER JOIN charge_type ON charge_type.charge_type_id = medi_dict.charge_type_id" + \
        " INNER JOIN dose_type ON dose_type.dose_type_id = medi_mark.dose_type_id" + \
        " INNER JOIN medi_pass ON medi_pass.medi_pass = pres_medi_out.medi_pass" + \
        " INNER JOIN medi_unit unit ON pres_medi_out.unit_id = unit.medi_unit_id" + \
        " INNER JOIN medi_unit ON pres_medi_out.medi_unit_id = medi_unit.medi_unit_id" + \
        " INNER JOIN use_type ON pres_medi_out.use_type_id = use_type.use_type_id" + \
        " WHERE pres_out.pres_id = '{}'".format(presId)
    db = mssql.ZgqMssql(config.get('db'))
    rxdrugdetail = db.query(sql)
    db.close()
    return rxdrugdetail
    
# 根据pres_id生成处方上传数据
def input_by_presid__7101(presId:str):
    ret = {
        "data": data_by_presid__7101(presId),
        "rxdrugdetail":rxdrugdetail_by_presid__7101(presId),
        "mdtrtinfo":mdtrtinfo_by_presid__7101(presId),
        "diseinfo":diseinfo_by_presid__7101(presId)
    }
    return ret

# 保存反馈信息
def savePres7101(presId,data):
    saveData = {
        "hi_rxno": data['output']['data']['hi_rxno'],
        "rx_trace_code": data['output']['data']['rx_trace_code'],
        "msg7101": json.dumps(data)
    }
    try:
        db = mssql.ZgqMssql(config.get('db'))
        sql = db.update('pres_out', saveData, 'pres_id = \'' + presId + '\'', False)
        db.commit();
        db.close()
    except:
        traceback.print_exc()
    return 1

def yb_7101(presId:str, x_token: str):
    ret = {"code": 201, "msg": 'fail', "data": {} }
    # 获取操作人员信息
    userInfo = certifyToken(x_token)
    if userInfo.get('result'):
        opter = userInfo['username']
        opter_name = userInfo['name']
        # 获取单位信息
        unitInfo = get_yb_info()
        # 医保电子处方上传接口地址
        url = unitInfo['prefix'] + 'fsi/api/opsp/7101'
        # 人员签到，获取签到码
        sign = yb_9001(unitInfo['hospitalCode'],unitInfo['hospitalName'],unitInfo['hospitalPasswd'],opter,opter_name)
        # 调用7101接口
        for _ in range(1):  # 循环指定次数
            post_data = get_public(unitInfo['hospitalCode'],unitInfo['hospitalName'],opter,opter_name)
            post_data['infno'] = '7101'  # 交易编号 ，交易编号详见接口列表
            post_data['input'] = input_by_presid__7101(presId) # 业务入参
            post_str = json.dumps(post_data)
            sign = get_sign(post_str, unitInfo['hospitalPasswd'], str(int(time.time())))
            headers = {'x-ca-key': unitInfo['hospitalCode'], 'x-ca-signature': sign, 'Content-Type':'application/json'}
            try:
                with requests.post(url, post_str, headers=headers) as req:
                    ret['data'] = req.json()
                    if ret['data']['infcode']==0:
                        ret['code'] = 200
                        ret['msg'] = '' #req['err_msg']
                        # 保存返馈结果到数据库
                        savePres7101(presId,ret['data'])
                    else:
                        ret['code'] = 201
                        ret['msg'] = ret['data']['err_msg']
                    break
            except:
                traceback.print_exc()
                time.sleep(0.2)
    else:
        ret = userInfo.get('ret')
    return ret
    
# 保存反馈信息
def savePres7104(data,returnData):
    saveData = {
        "msg7104": json.dumps(returnData)
    }
    #print('rxStasCodg:',returnData['output']['rxStasCodg'])
    if returnData['infcode'] == 0:
        saveData["rx_stas_codg"]=returnData['output']['rxStasCodg']
        saveData["hi_rxno"]=''

    try:
        db = mssql.ZgqMssql(config.get('db'))
        sql = db.update('pres_out', saveData, 'pres_id = \'' + data['pres_id'] + '\'', False)
        db.commit();
        db.close()
    except:
        traceback.print_exc()
    return 1

# 电子处方撤消
def yb_7104(data:Dict, x_token: str):
    ret = {"code": 201, "msg": 'fail', "data": {} }
    # 获取操作人员信息
    userInfo = certifyToken(x_token)
    if userInfo.get('result'):
        opter = userInfo['username']
        opter_name = userInfo['name']
        # 获取单位信息
        unitInfo = get_yb_info()
        # 医保电子处方上传接口地址
        url = unitInfo['prefix'] + 'fsi/api/opsp/7104'
        # 人员签到，获取签到码
        sign = yb_9001(unitInfo['hospitalCode'],unitInfo['hospitalName'],unitInfo['hospitalPasswd'],opter,opter_name)
        # 调用7104接口
        for _ in range(1):  # 循环指定次数
            post_data = get_public(unitInfo['hospitalCode'],unitInfo['hospitalName'],opter, opter_name)
            post_data['infno']='7104'  # 交易编号 ，交易编号详见接口列表
            # 7104入参
            input={ 
                "data": {
                    "hi_rxno":data['hi_rxno'],                  #医保处方编号,字符型30
                    "dr_code":userInfo['country_code'],         #撤销医师的医保医师代码,字符型	20
                    "undo_dr_name":userInfo['name'],            #撤销医师姓名,字符型	50
                    "undo_dr_cert_type":userInfo['cert_type'],  #data['undo_dr_cert_type'], #撤销医师证件类型,字符型	6	Y	Y	参照人员证件类型
                    "undo_dr_certno":userInfo['certno'],        #data['undo_dr_certno'],    #撤销医师证件号码,字符型	50
                    "undo_rea":data['undo_rea'],                #撤销原因描述,数值型	200
                    "undo_time":datetime.now().strftime("%Y-%m-%d %H:%M:%S")          #撤销时间	日期时间型
                }
            }
            post_data['input']=input # 业务入参
            post_str=json.dumps(post_data)
            sign=get_sign(post_str,unitInfo['hospitalPasswd'],str(int(time.time())))
            headers={'x-ca-key':unitInfo['hospitalCode'],'x-ca-signature':sign,'Content-Type':'application/json'}
            try:
                with requests.post(url,post_str,headers=headers) as req:
                    ret['data']=req.json()
                    ret['msg']=ret['data']['err_msg']
                    savePres7104(data,ret['data'])
                    if ret['data']['infcode']==0:
                        ret['code']=200
                    break
            except:
                traceback.print_exc()
                time.sleep(0.2)
    else:
        ret = userInfo.get('ret')
    return ret

if __name__ == '__main__':
    #ret = yb_7101('900040000024','MTczNjQ4MjQ5Ny42OTk1NTc4Ojk0ZGQzYWEzODU5MTJhYThlMzU1ZjFkMDdmYTIyNWZiMjQzNzkyNjg=')
    data={
        "pres_id":"900040000024",           #处方编号
        "hi_rxno":"330120250111000008899327",           #医保处方编号	字符型	30		Y	
        "dr_code":"D330109038856",           #撤销医师的医保医师代码	字符型	20		Y	
        "undo_dr_name":"傅伟浩",      #撤销医师姓名	字符型	50		Y	
        "undo_dr_cert_type":"01", #撤销医师证件类型	字符型	6	Y	Y	参照人员证件类型
        "undo_dr_certno":"339005198010100333",    #撤销医师证件号码	字符型	50		Y	
        "undo_rea":"撤销原因描述",#撤销原因描述	数值型	200		Y	
        "undo_time":datetime.now().strftime("%Y-%m-%d %H:%M:%S")#撤销时间	日期时间型			Y	   
    }
    ret=yb_7104(data,'MTczNjc1MDY0MC40NDI4MDEyOjEwMGU5M2I2MGEzZjFkZjY4MzczZWQwZDI3Mjc1N2M4YWVlNTI3ZTk=')
    print('ret',ret)
    # ret_data = get_report('330000171694255746203224236218')
    # print(json.dumps(ret_data, ensure_ascii=False, indent=2))


